﻿using iot.Commons;
using iot.Models;
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace iot.Protocol.Modbus
{
    internal class ModbusTcpClientNet : IPrototol
    {

        private TcpClient? _tcpClient;

        private ModbusIpMaster? master;

        private int _sendTimeout = 3000;

        private int _receiveTimeout = 3000;

        private bool connected = false;

        public bool Connect(PlcInfo plcInfo)
        {
                    _tcpClient = new TcpClient();
                    _tcpClient.SendTimeout = _sendTimeout;
                    _tcpClient.ReceiveTimeout = _receiveTimeout;
                    _tcpClient.Connect(IPAddress.Parse(plcInfo.IP), int.Parse(plcInfo.PORT));
            if (!_tcpClient.Connected)
            {
                throw new Exception("连接失败");
            }
            master = ModbusIpMaster.CreateIp(_tcpClient);
            this.connected = true;
                return true;
        }

        public bool Disconnect()
        {
                master?.Dispose();
                connected = false;
                return true;
        }

        // Fix for CS1503: Convert the string `pointInfo.address` to `ushort` using `ushort.Parse`.
        // Ensure that the `address` property in `PointInfo` contains a valid numeric string that can be parsed to `ushort`.

        public object ReadData(PointInfo pointInfo, DataFormatEnum dataFormatEnum)
        {
            // Ensure `pointInfo` and `pointInfo.address` are not null to avoid CS8602
            if (pointInfo == null || pointInfo.address == null)
            {
                throw new ArgumentNullException(nameof(pointInfo), "PointInfo or its address cannot be null.");
            }

            // Convert the address from string to ushort to fix CS0030
            if (!ushort.TryParse(pointInfo.address, out ushort address))
            {
                throw new FormatException($"The address '{pointInfo.address}' is not a valid ushort value.");
            }

            // Ensure `DetectionValue_Qty` is not null and convert it to ushort to fix CS1503
            if (!pointInfo.DetectionValue_Qty.HasValue)
            {
                throw new ArgumentNullException(nameof(pointInfo.DetectionValue_Qty), "DetectionValue_Qty cannot be null.");
            }

            ushort detectionValueQty = (ushort)pointInfo.DetectionValue_Qty.Value;

            // Read holding registers
            if (master == null)
            {
                throw new InvalidOperationException("Modbus master is not initialized.");
            }

            ushort[] registers = master.ReadHoldingRegisters(address, detectionValueQty);

            if (registers == null)
            {
                throw new Exception("读取寄存器失败");
            }

            // Convert the registers to the desired data type
            string? value = null;
            string[] values = Array.Empty<string>();

            switch (pointInfo.pointType)
            {
                case nameof(DataTypeEnum.INT):
                    int v = ModbusReadConverter.ToInt32(registers, dataFormatEnum.ToString());
                    value = v.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.UINT):
                    uint uv = ModbusReadConverter.ToUInt32(registers, dataFormatEnum.ToString());
                    value = uv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.FLOAT):
                    float fv = ModbusReadConverter.ToFloat(registers, dataFormatEnum.ToString());
                    value = fv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.DOUBLE):
                    double dv = ModbusReadConverter.ToDouble(registers, dataFormatEnum.ToString());
                    value = dv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.SHORT):
                    short sv = ModbusReadConverter.ToInt16(registers[1]);
                    value = sv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.USHORT):
                    ushort usv = ModbusReadConverter.ToUInt16(registers[1]);
                    value = usv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.LONG):
                    long lv = ModbusReadConverter.ToInt64(registers, dataFormatEnum.ToString());
                    value = lv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.ULONG):
                    ulong ulv = ModbusReadConverter.ToUInt64(registers, dataFormatEnum.ToString());
                    value = ulv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                case nameof(DataTypeEnum.BOOL):
                    bool bv = ModbusReadConverter.ToBool(registers[1], 1);
                    value = bv.ToString();
                    values = value.Split(',') ?? Array.Empty<string>();
                    break;
                default:
                    throw new NotSupportedException($"Data type '{pointInfo.pointType}' is not supported.");
            }
            pointInfo.Value = value.ToString();
            pointInfo.Values = values;
            return value;
        }

        public bool WriteData(PointInfo pointInfo, object d, DataTypeEnum dataTypeEnum)
        {
            // Ensure `pointInfo` and its properties are not null to avoid CS8602
            if (pointInfo == null || pointInfo.address == null || pointInfo.Value == null)
            {
                throw new ArgumentNullException(nameof(pointInfo), "PointInfo, its address, or its Value cannot be null.");
            }

            // Parse and write the register value
            if (master == null)
            {
                throw new InvalidOperationException("Modbus master is not initialized.");
            }
            try
            {
                master.WriteSingleRegister(ushort.Parse(pointInfo.address), ushort.Parse(pointInfo.Value));
                return true;
            }
            catch (Exception ex) {
                throw new Exception(ex.Message);
            }
        }
      
        // 辅助方法：将寄存器值转换为16位整数
        public static short GetInt16(ushort[] registers, int index)
        {
            return (short)((registers[index] << 16) | registers[index + 1]);
        }

        // 辅助方法：将寄存器值转换为16位无符号整数
        public static ushort GetUInt16(ushort[] registers, int index)
        {
            return (ushort)((registers[index] << 16) | registers[index + 1]);
        }

        // 辅助方法：将寄存器值转换为32位整数
        public static int GetInt32(ushort[] registers, int index)
        {
            return (registers[index] << 16) | registers[index + 1];
        }

        // 辅助方法：将寄存器值转换为32位无符号整数
        public static uint GetUInt32(ushort[] registers, int index)
        {
            return (uint)((registers[index] << 16) | registers[index + 1]);
        }

        // 辅助方法：将寄存器值转换为64位整数
        public static long GetInt64(ushort[] registers, int index)
        {
            var bytes = new byte[8];
            Buffer.BlockCopy(registers, index * 2, bytes, 0, 8);
            Array.Reverse(bytes); // Big-endian to little-endian
            return BitConverter.ToInt64(bytes, 0);
        }

        // 辅助方法：将寄存器值转换为64位无符号整数
        public static ulong GetUInt64(ushort[] registers, int index)
        {
            var bytes = new byte[8];
            Buffer.BlockCopy(registers, index * 2, bytes, 0, 8);
            Array.Reverse(bytes); // Big-endian to little-endian
            return BitConverter.ToUInt64(bytes, 0);
        }

        // 辅助方法：将寄存器值转换为浮点数
        public static float GetFloat(ushort[] registers, int index)
        {
            var bytes = new byte[4];
            Buffer.BlockCopy(registers, index * 2, bytes, 0, 4);
            Array.Reverse(bytes); // Big-endian to little-endian
            return BitConverter.ToSingle(bytes, 0);
        }

        // 辅助方法：将寄存器值转换为双精度浮点数
        public static double GetDouble(ushort[] registers, int index)
        {
            var bytes = new byte[8];
            Buffer.BlockCopy(registers, index * 2, bytes, 0, 8);
            Array.Reverse(bytes); // Big-endian to little-endian
            return BitConverter.ToDouble(bytes, 0);
        }

        // 辅助方法：将寄存器值转换为十六进制字符串
        public static string GetHexString(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return BitConverter.ToString(bytes).Replace("-", "");
        }

        // 辅助方法：将寄存器值转换为十进制字符串
        public static string GetDecimalString(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return BitConverter.ToInt32(bytes, index * 2).ToString();
        }

        // 辅助方法：将寄存器值转换为ASCII字符串
        public static string GetAsciiString(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return Encoding.ASCII.GetString(bytes);
        }

        // 辅助方法：将寄存器值转换为BCD字符串
        public static string GetBcdString(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return BitConverter.ToString(bytes).Replace("-", "");
        }

        // 辅助方法：将寄存器值转换为BCD整数
        public static int GetBcdInt(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return BitConverter.ToInt32(bytes, index * 2);
        }

        // 辅助方法：将寄存器值转换为布尔
        public static bool GetBool(ushort[] registers, int index)
        {
            var bytes = new byte[registers.Length * 2];
            Buffer.BlockCopy(registers, 0, bytes, 0, bytes.Length);
            return BitConverter.ToBoolean(bytes, index * 2);
        }
    }

    public class ModbusException : Exception
    {
        public byte ExceptionCode { get; }

        public ModbusException(string message) : base(message) { }
        public ModbusException(string message, byte exceptionCode) : base(message)
        {
            ExceptionCode = exceptionCode;
        }
    }
}
